import sys
import re
import os
import json
import serial
import serial.tools.list_ports
import struct
import logging
import datetime
import time

#from configparser import ConfigParser
from threading import Timer
from pycrc.algorithms import Crc

from PySide6 import QtWidgets
from PySide6 import QtNetwork
from PySide6 import QtCore

from PySide6.QtCore import Qt 
from PySide6.QtCore import QByteArray
from PySide6.QtGui import *
from PySide6.QtCore import *
from PySide6.QtWidgets import *

from enum import Enum
class checkEnum(Enum):
    checkNone = 0
    checkSN = 1


ports_list = []
for port in serial.tools.list_ports.comports():
    ports_list.append(port.name)
print(ports_list)

baudrate = 9600
bytesize = 8
stopbits = 1
com_parity = 'N'



DateRegTx = ["00 07","00 11","00 24","00 37","00 4A","00 64"]  #"00 67" date&time

'''
class RxThread(QThread):
    def __init__(self, parent=None):
        super().__init__(parent=parent)
        self.is_paused = True

    def pause_thread(self):
        self.is_paused = True

    def resume_thread(self):
        self.is_paused = False

    def run(self):
        while True:
            while self.is_paused:
            self.msleep(1000)
'''
logger = logging.getLogger(__name__)

class MyWidget(QtWidgets.QWidget):
    logStr = QtCore.Signal(str)
    errStr = QtCore.Signal(str)
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.setWindowTitle("Sensor Setting")
        self.com_open = False
        self.resize(700, 350)        

        self.ser = serial.Serial()
        self.IsRx = False

        self.line1 = ""
        self.lines = ""

        self.checkStatus = checkEnum.checkNone.value
        self.checkSNTime = datetime.datetime.now()
        
        self.setup_ui()
        self.logStr.connect(self.ChangeLogText)
        self.errStr.connect(self.ErrorMesgBox)
        self.crc = Crc(width = 16, poly = 0x8005,
            reflect_in = True, xor_in = 0xffff,
            reflect_out = True, xor_out = 0x0000)
    
    def __del__(self):
        print("finish")
        print(self.IsRx)
        #if (self.ser.isOpen()):
        #    self.ser.close()
        #t1.cancel() 

    def _crc485(self,strData) -> str:
        if (len(strData) != 0):
            try :
                dataTx = bytes.fromhex(strData)
                temp1 = self.crc.bit_by_bit(dataTx)
                temp2 = "0x{0:04X}".format(temp1)
                ttt = temp2[4] + temp2[5] + " " + temp2[2] + temp2[3]
                return ttt
                #self.TxCRC.setText(ttt)
            except:
                pass
        return ""

    def _sendTx_woCRC(self,strTx) -> None:
        try:
            crcTx = self._crc485(strTx)
            logger.info("Tx--- " + strTx + " " + crcTx)
            dataTx = bytes.fromhex(strTx + " " + crcTx)
            self.ser.write(dataTx)
            time.sleep(0.5)
        except:
            print("parse hex data error")
            pass

    
    def _WriteSN(self) -> None:
        self.logStr.emit("Write SN")
        strTx = "D2 06 00 01"
        snText = self.snLineEdit.text()
        snTx = "{0:04X}".format(int(snText))
        strTx += " " + snTx[-4:-2] + " " + snTx[-2:]
        self._sendTx_woCRC(strTx)
    
    def _WriteDateTime(self) -> None:
        self.logStr.emit("Write DateTime...")
        nowTime = datetime.datetime.now()
        yearHex = "0x{0:04X}".format(nowTime.year)
        yearTx = yearHex[2] + yearHex[3] + " " + yearHex[4] + yearHex[5]
        monthTx = "{0:02X}".format(nowTime.month)
        dayTx = "{0:02X}".format(nowTime.day)
        hourTx = "{0:02X}".format(nowTime.hour)
        minuteTx = "{0:02X}".format(nowTime.minute)
        secondTx = "{0:02X}".format(nowTime.second)

        for dTx in DateRegTx:
            strTx = "D2 10 " + dTx + " 00 02 04 " + yearTx + " " + monthTx + " " + dayTx
            self._sendTx_woCRC(strTx)
            #print(strTx)
        strTx = "D2 10 00 67 00 04 08" + yearTx + " " + monthTx + " " + dayTx
        strTx += " 00 " + hourTx + " " + minuteTx + " " + secondTx
        self._sendTx_woCRC(strTx)
        #print(strTx)
 
    def _WriteConfig(self) -> None:
        self.logStr.emit("Write Config file...")
        for line in self.lines:
            strTx = line.replace('\r','').replace('\n','')
            self._sendTx_woCRC(strTx)  

    def _CheckSN(self) -> None:
        self.checkStatus = checkEnum.checkSN.value
        strTx = "D2 03 00 01 00 01"
        self.checkSNTime = datetime.datetime.now()
        self._sendTx_woCRC(strTx)

    def Checkdata(self) -> None:
        self.logStr.emit("Check data...")
        self._CheckSN()

    def _HandleRxData(self, RxStr) -> None:
        # should check CRC firstly
        #if (self.checkStatus == checkEnum.checkSN.value) :
        #    RxStr = "D2 06 00 01 30 39 1F BB"
        RxPlayload = RxStr[:-5]
        crcRx = self._crc485(RxPlayload)
        #print("---check CRC RxCRC is:" + RxStr[-5:] + "   and calcrc is:"+ crcRx)
        if ( RxStr[-5:] != crcRx) :
            logger.info("--- Check CRC Error! " + RxStr )
            self.logStr.emit("Check CRC Error")
            self.logStr.emit(RxStr)
            self.errStr.emit("Receive CRC Error")
            self.checkStatus = checkEnum.checkNone.value
            return

        match self.checkStatus:
            case checkEnum.checkNone.value:
                print("do nothing")
            case checkEnum.checkSN.value:
                RxSN = RxStr[-11:-5].replace(" ","")
                InputSNTxT = self.snLineEdit.text()[-4:]
                InputSN = "{0:04X}".format(int(InputSNTxT))
                if ( RxSN == InputSN):
                    logger.info("--- Check Succesful on SN:" + self.snLineEdit.text())
                    self.logStr.emit("Check SN Succesful")
                else :
                    logger.info("--- Check Fail on SN:" + self.snLineEdit.text())
                    self.errStr.emit("Check SN Error")
                print("Check SN")

        self.checkStatus = checkEnum.checkNone.value

    def setup_ui(self) -> None:
    
        labe1 = QtWidgets.QLabel("com", self) 
        labe1.move(50, 20)
        labe1.resize(70,30)

        self.comlist = QtWidgets.QComboBox(self)
        self.comlist.addItems(ports_list)
        self.comlist.setGeometry(50,50,80,30)
        
        self.combutton = QtWidgets.QPushButton("Open", self)
        self.combutton.move(150, 40)
        self.combutton.resize(60,40)
        self.combutton.clicked.connect(self.OpenCom)

        labe2= QtWidgets.QLabel("SN", self) 
        labe2.move(50, 120)
        labe2.resize(70,30)

        self.snLineEdit = QtWidgets.QLineEdit(self)
        self.snLineEdit.setMaxLength(25)
        self.snLineEdit.move(120,120)
        self.snLineEdit.resize(250,30) 
        
        self.snValidator = QRegularExpressionValidator()
        #self.snValidator.setRegularExpression('[0-9a-fA-F]{6}')
        #self.snLineEdit.setValidator(self.snValidator)

        self.Loadbutton = QtWidgets.QPushButton("LoadConfig", self)
        self.Loadbutton.move(50, 200)
        self.Loadbutton.resize(80,50)
        #self.Loadbutton.setEnabled(False)
        self.Loadbutton.clicked.connect(self.LoadConfig)

        self.configFile = QtWidgets.QLineEdit("",self)
        self.configFile.setMaxLength(200)
        self.configFile.move(140,200)
        self.configFile.resize(230,50)   
        self.configFile.setEnabled(False)

        self.Txbutton = QtWidgets.QPushButton("Setting", self)
        self.Txbutton.move(220, 270)
        self.Txbutton.resize(80,50)
        self.Txbutton.setEnabled(False)
        self.Txbutton.clicked.connect(self.TxData)

        '''
        self.clearlogbutton = QtWidgets.QPushButton("Clear", self)
        self.clearlogbutton.move(490, 30)
        self.clearlogbutton.resize(80,30)
        self.clearlogbutton.clicked.connect(self.clearlog)
        '''

        self.logEdit = QtWidgets.QTextEdit(self)
        self.logEdit.setGeometry(420,50,250,200)
        self.logEdit.setEnabled(False)


    @QtCore.Slot(str)
    def ChangeLogText(self,logString) -> None:
        tmpText = self.logEdit.toPlainText() + logString + "\n"
        self.logEdit.setPlainText(tmpText)
        self.repaint()
    
    @QtCore.Slot(str)
    def ErrorMesgBox(self, errString) -> None:
        QMessageBox.critical(None,"Error",errString)

    
    def readcomData(self) -> None:
        print("read run")
        if (self.ser.isOpen()):
            RxData = self.ser.read_all()            
            RxString = ' '.join(struct.pack('B', x).hex() for x in RxData)
            if ( len(RxString) != 0 ) :
                logger.info("Rx--- " + RxString.upper())
                self._HandleRxData(RxString.upper())
               
            #self._HandleRxData(RxString.upper())   # debug purpose, need delete later 
            curTime = datetime.datetime.now()
            if ( self.checkStatus == checkEnum.checkSN.value) :
                if ((curTime - self.checkSNTime) > datetime.timedelta(seconds = 5)):
                    #print("TimeOut")
                    self.checkStatus = checkEnum.checkNone.value 
                    self.errStr.emit("Timeout error, please check sys!")


        if (self.IsRx):
            global t1
            t1 = Timer(0.5, self.readcomData)
            t1.start()
    
    
    def OpenCom(self) -> None:
        choice_com = self.comlist.currentText()
        if len(choice_com) != 0 :
            self.com_open = not self.com_open
            if (self.com_open) :
                self.combutton.setText("Close")

                self.ser.port = choice_com
                self.ser.baudrate = baudrate
                self.ser.bytesize = bytesize
                self.ser.stopbits = stopbits
                self.ser.parity = com_parity
                self.ser.open()
                if (self.ser.isOpen()):
                    print("open com ok")
                    self.Txbutton.setEnabled(True)
                    self.IsRx = True
                    self.readcomData()
                else:
                    print("open com fail")

            else :
                self.combutton.setText("Open")
                self.ser.close()
                self.logEdit.setPlainText("")
                self.Txbutton.setEnabled(False)
                self.IsRx = False
                print("com closed")

    def LoadConfig(self) -> None:
        filePath,_ = QFileDialog.getOpenFileName(self,"Open config file","c:\\","configfile(*.ini)" )
        self.configFile.setText(filePath)
        if(filePath):
            file = open(self.configFile.text(),'r')
            self.line1 = file.readline().replace('\r','').replace('\n','')
            self.lines = file.readlines()
            file.close()
    
    def clearlog(self) -> None:
        self.logEdit.setPlainText("")

    def TxData(self) -> None:
        self.logEdit.setPlainText("")
        if (self.line1 == self.snLineEdit.text()[-6:-4]):
            self.Txbutton.setEnabled(False)
            logger.info("--- Start write device on SN:" + self.snLineEdit.text())
            tmpText = "start dev on " + self.snLineEdit.text()
            self.logStr.emit(tmpText)

            self._WriteSN()
            self._WriteDateTime()
            self._WriteConfig()

            logger.info("--- Finish write device on SN:" + self.snLineEdit.text())  
            self.logStr.emit("Finish write device")

            self.Checkdata()
            logger.info("--- Finish check device data on SN:" + self.snLineEdit.text())
            self.Txbutton.setEnabled(True)
        else:
            self.errStr.emit("config file or SN wrong")

            
    
    def closeEvent(self,event):
        print("closeEvent")
        self.IsRx = False
        time.sleep(2)
        if (self.ser.isOpen()):
            self.ser.close()

        #sys.exit(0)
 

if __name__ == "__main__":
    logging.basicConfig(filename='Self_Sensor.log', level=logging.INFO, format = "%(asctime)s - %(message)s")
    app = QtWidgets.QApplication(sys.argv)
    window = MyWidget()
    window.show()
    sys.exit(app.exec())
